Introduction to scRNAseq and data pre-processing


1. FASTQ Files, genome alignments, and deconvolution of cells

This module of the CSTMI workshop provides an overview of key quality control (QC) metrics and pre-processing steps in the initial analysis of single-cell transcriptomic data beginning after alignment to a reference genome. Detailed tutorials for processing raw BCL and FASTQ files, performing genome alignments, and deconvoluting cells from empty GEMs/wells are available through various resources, we will focus on understanding what is captured and represented in single-cell data.

See below for links to alignment and preliminary analysis tutorials:

  • Vendor-specific pipeline resources (e.g., 10X Genomics)

  • Galaxy

  • Google colab hands-on experience (under construction)

Most of these pipelines will provide a summary report such as this 10X Genomics report or this one from Parse Biosciences. These reports provide the most basic information to assess the quality of the data starting with the knee plot.

1.1. Understanding the ‘knee plot’

Upon receiving aligned single-cell/nuclei sequencing data, one of the initial plots to examine is the ‘knee plot,’ commonly generated during analysis pipelines. This plot helps distinguish between barcodes representing cells and those representing background noise or empty Gel Emulsion beads (GEMs).

Gel Emulsion beads (GEMs) are microfluidic droplets used in platforms like 10X Genomics, each potentially containing a cell or nuclei. Distinguishing between GEMs with cells and empty GEMs is crucial for accurate analysis.

The knee plot visually depicts how a threshold is set to differentiate between cell barcodes and background noise. In platforms like 10X Genomics, cells within GEMs produce a larger number of unique mRNA molecules (UMIs) compared to empty GEMs. The knee plot typically shows a sudden drop (knee) in UMIs, indicating the separation between GEMs containing cells and those lacking them.

Figure from Danielski, K. (2023). Guidance on Processing the 10x Genomics Single Cell Gene Expression Assay. In: Calogero, R.A., Benes, V. (eds) Single Cell Transcriptomics. Methods in Molecular Biology, vol 2584. Humana, New York, NY. https://doi.org/10.1007/978-1-0716-2756-3_1

Interpreting the knee plot involves identifying this knee point. A distinct knee suggests successful separation, while the absence of a clear knee could indicate issues such as low RNA integrity or high ambient RNA contamination.

Figure from https://isoseq.how/umi/cell-calling.html

Now that we have a general idea of what to look for in a summary report, lets use real data to start exploring more advanced QC metrics which require additional analysis steps.


2. Setting up our R environment for data exploration

In preparation for the following hands-on experiences we will set up an R environment which has everything we need.

2.1. Load libraries

suppressPackageStartupMessages({
  library(Seurat)
  library(ggplot2)
  library(tidyr)
  library(scDblFinder)
  library(SingleCellExperiment)
})

2.2. Import data

For this tutorial we will use raw unprocessed data from human peripheral blood mononuclear cells (PBMCs) generated using the 10X Genomics platform which can be download from the Seurat page. These data should be downloaded, extracted, and placed in the downloads folder for this workshop.

# Load the PBMC dataset
pbmc.data <- Read10X(data.dir = "./downloads/filtered_gene_bc_matrices/hg19/")
# Initialize the Seurat object with the raw (non-normalized data).
seurat_object <- CreateSeuratObject(counts = pbmc.data, project = "pbmc3k", 
                                    min.cells = 3, min.features = 200)
Warning: Feature names cannot have underscores ('_'), replacing with dashes ('-')

Note: We apply some preliminary filters as we know that GEMs which very low numbers of genes or genes which are barely detected are unlikely to be actual cells.

2.3. Check that everything was loaded correctly

print(seurat_object)
An object of class Seurat 
13714 features across 2700 samples within 1 assay 
Active assay: RNA (13714 features, 0 variable features)
 1 layer present: counts

3. Overview of Single-cell Data Structure

To better understand how to work with single-cell/nuclei RNA sequencing data we will take a deep dive into how datasets are typically structured. In the diagram below we illustrate how droplet-based scRNAseq approaches separate cells and their mRNAs. But how does this translate into data?

Generated using biorender.com
Generated using biorender.com

3.1. The Gene x Cell Matrix

# Raw counts in a seurat object is saved in the assays-RNA-counts slot 
raw_counts = as.matrix(seurat_object@assays$RNA@layers$counts)
rownames(raw_counts) = rownames(seurat_object)
colnames(raw_counts) = colnames(seurat_object)

# Lets look at the first 4 columns and rows
print(raw_counts[1:4,1:4])
              AAACATACAACCAC-1 AAACATTGAGCTAC-1 AAACATTGATCAGC-1 AAACCGTGCTTCCG-1
AL627309.1                   0                0                0                0
AP006222.2                   0                0                0                0
RP11-206L10.2                0                0                0                0
RP11-206L10.9                0                0                0                0

What does this mean?

Columns: The columns represent a unique barcode. As we can see below, this barcode marks an individual cell which is how we can identify each one. So in this object, the columns are the cells

Generated in biorender.com and adapted from Danielski, K. (2023). Guidance on Processing the 10x Genomics Single Cell Gene Expression Assay. In: Calogero, R.A., Benes, V. (eds) Single Cell Transcriptomics. Methods in Molecular Biology, vol 2584. Humana, New York, NY. https://doi.org/10.1007/978-1-0716-2756-3_1
Generated in biorender.com and adapted from Danielski, K. (2023). Guidance on Processing the 10x Genomics Single Cell Gene Expression Assay. In: Calogero, R.A., Benes, V. (eds) Single Cell Transcriptomics. Methods in Molecular Biology, vol 2584. Humana, New York, NY. https://doi.org/10.1007/978-1-0716-2756-3_1

Rows: We see that the first few rows have names such as RP11-206L10.9. A quick google search will tell you that this is a human gene (Ensembl ID - ENSG00000237491). So we see in this object that the rows represent the genes.

3.2. Counting the number of cells and genes

Single-cell transcriptomic analysis tools (e.g., Seurat) does a lot of the heavy lifting for us when it comes to counts and other summary data information. For example, the following code will tell you the number of genes and cells, as well store raw, normalized, and scaled data, dimension reductions, and other analyses which may have been performed.

print(seurat_object)
An object of class Seurat 
13714 features across 2700 samples within 1 assay 
Active assay: RNA (13714 features, 0 variable features)
 1 layer present: counts

This indicates that we have 13,714 genes (features) across 2,700 samples.

However, we can also extract that information from the count table:

print(paste0('Cells: ', ncol(raw_counts)))
[1] "Cells: 2700"
print(paste0('Features:', nrow(raw_counts)))
[1] "Features:13714"

These numbers should match!

3.3. A deeper dive into counts

3.3a. What is sequencing depth?

# Calculate the total reads in each individual cell
read_depths = colSums(raw_counts)

# How are they distributed?
hist(read_depths, main = "Histogram of read depth", 
     xlab = "Depth", ylab = "Frequency",
     breaks = 200)

This histogram shows that the number of mRNA transcripts detected in each cell ranged from 546 to 15,818, but that most were around 2,000. So what might account for these differences?

  • Cell types: Some cell types may simply just express more mRNA than others. For example, Hepatocytes are known to contain a lot of RNA while others may not.

  • Stochastic gene expression: Fluctuations in transcription and degradation from normal biological processes, cell states, and other factors in individual cells which have very low amounts of RNA to begin can appear as big differences.

  • Technical variability: Sequencing depth, assay kits, and other technical factors may contribute to these differences.

The zero-inflation debate: In the early days of scRNAseq it was a widely accepted that data was zero-inflated, i.e., it had more zero’s than it should because of technical challenges. Today, we better understand that the large number of zero’s are not only technical, but in fact does reflect true biology as well. The following manuscript delves deeper into the debate: Jiang et al., 2022

3.3b. Another way to look at depth

Sequencing depth ultimately drives the ability to detect individual genes in a cell or sample (bulk RNA seq). As more reads are performed on a sample, the probability of capturing a lower abundance gene increases. Therefore, sequencing depth can have a significant impact on the end results of your sequencing results.

So why not sequence as deep as possibe? As with anything, we need to balance cost with value. The following figures are standard outputs from scRNAseq reports including those linked above.

As you can see, the more reads we have per cell, the smaller the slop for capturing new genes gets. Therefore, the difference between capturing 80% of genes and 100% could mean 2, 10, or even >100X the amount of sequencing. Is it truly worth it for capturing a gene who expression is so low? This is why we limit our depth for sequencing and why most datasets produced with the sample platform will tend to detect a similar number of genes.

3.4. Common Storage Formats

3.4a. Sparse tables (raw)

We previously mentioned that single-cell/nuclei transcriptomic data has a lot of zeros. Lets actually look at the count distribution so you can see:

# We wan to collapse all the values into a vector of values
mat_values <- as.vector(raw_counts)

# Plot histogram
hist(mat_values, main = "Histogram of Matrix Values", xlab = "Value", ylab = "Frequency", breaks = 600, xlim = c(0,10))

This is why you will almost always find raw scRNAseq data saved as a .mtx (sparse matrix) file or format.

Here we will download publicly accessible sparse matrices from the Gene Expression Omnibus, an excellent resource for obtaining raw data if you want to reuse and reanalyze existing data. This dataset is from single-nuclei RNA sequencing of livers from mice treated with the environmental contaminant 2,3,7,8-tetrachlorodibenzo-p-dioxin (TCDD).

#download.file('https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE184506&format=file&file=GSE184506%5Fsct%2Emtx%2Egz', './downloads/matrix.mtx.gz')
system("gunzip ./downloads/matrix.mtx.gz")
[1] 127
#download.file('https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE184506&format=file&file=GSE184506%5Fsct%2Ebarcodes%2Etsv%2Egz', './downloads/barcodes.tsv.gz')
system("gunzip ./downloads/barcodes.tsv.gz")
[1] 127
#download.file('https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE184506&format=file&file=GSE184506%5Fsct%2Egenes%2Etsv%2Egz', './downloads/genes.tsv.gz')
system("gunzip ./downloads/genes.tsv.gz")
[1] 127
# Read the first few lines of the file
lines <- readLines('./downloads/matrix.mtx', n = 10)

# Print the lines
cat(lines, sep = "\n")
%%MatrixMarket matrix coordinate real general
20569 131613 221295805
15 1 1.6094379124341003
20 1 1.3862943611198906
29 1 .6931471805599453
30 1 .6931471805599453
32 1 1.791759469228055
39 1 1.0986122886681096
49 1 1.3862943611198906
57 1 1.0986122886681096

As you can see, there is not a zero in sight here. After you read the first 2 summary lines you can see the following structure:

Feature/Gene Barcode/Cell Count/Value
Refers to the row number in the genes.tsv file Refers to the row number in the barcodes.tsv file Include all non-zero values of the dataset

Let’s see what this is here

# Read the specific line of the file
readLines('./downloads/genes.tsv', n = 15)[15]
[1] "Sntg1"
# Read the specific line of the file
readLines('./downloads/barcodes.tsv', n = 1)[1]
[1] "AAACCCACAACTTCTT_1"

From here we can see that the first row of data in the mtx file represent the gene Sntg1 for the cell with barcode AAACCCACAACTTCTT_1.

3.4b. Seurat object

Image from https://sib-swiss.github.io/single-cell-training/day1/day1-2_analysis_tools_qc.html

Seurat objects are probably the most commonly used to store and organize scRNAseq data for R users. So far this is what we have used for the published PBMC data which we imported in Section 2. Let’s take another closer look at this object:

print(slotNames(seurat_object)) # Lets examine all the slots
 [1] "assays"       "meta.data"    "active.assay" "active.ident" "graphs"       "neighbors"    "reductions"   "images"       "project.name" "misc"         "version"      "commands"     "tools"       

There is a lot of information, but the ones you will most commonly use if you do single-cell analyses in R are assays, meta.data, and reductions

  • Assays will hold the count data

  • Meta.data will hold the metadata for each individual cell/nuclei

  • Reductions will hold the dimension reductions for visualizations

3.4c. SingleCellExperiment

Another common format in R are SingleCellExperiments. They contain the same information, but are organized in a different manner.

image from https://bioconductor.org/books/3.13/OSCA.intro/the-singlecellexperiment-class.html

First, let’s convert our seurat object to SingleCellExperiment. Most formats can easily be interchanged:

sce = as.SingleCellExperiment(seurat_object)

Next, lets see what the structure looks like:

print(sce)
class: SingleCellExperiment 
dim: 13714 2700 
metadata(0):
assays(1): counts
rownames(13714): AL627309.1 AP006222.2 ... PNRC2.1 SRSF10.1
rowData names(0):
colnames(2700): AAACATACAACCAC-1 AAACATTGAGCTAC-1 ... TTTGCATGAGAGGC-1 TTTGCATGCCTCAC-1
colData names(4): orig.ident nCount_RNA nFeature_RNA ident
reducedDimNames(0):
mainExpName: RNA
altExpNames(0):

Here we can see:

  • rownames and rowData contain the gene information and metadata

  • colnames and colData contain the cell information and metadata

  • assays here also hold the count data

We can examine the pieces of data such as the cell metadata as follows:

print(colData(sce))
DataFrame with 2700 rows and 4 columns
                 orig.ident nCount_RNA nFeature_RNA    ident
                   <factor>  <numeric>    <integer> <factor>
AAACATACAACCAC-1     pbmc3k       2419          779   pbmc3k
AAACATTGAGCTAC-1     pbmc3k       4903         1352   pbmc3k
AAACATTGATCAGC-1     pbmc3k       3147         1129   pbmc3k
AAACCGTGCTTCCG-1     pbmc3k       2639          960   pbmc3k
AAACCGTGTATGCG-1     pbmc3k        980          521   pbmc3k
...                     ...        ...          ...      ...
TTTCGAACTCTCAT-1     pbmc3k       3459         1153   pbmc3k
TTTCTACTGAGGCA-1     pbmc3k       3443         1224   pbmc3k
TTTCTACTTCCTCG-1     pbmc3k       1684          622   pbmc3k
TTTGCATGAGAGGC-1     pbmc3k       1022          452   pbmc3k
TTTGCATGCCTCAC-1     pbmc3k       1984          723   pbmc3k

3.4d. AnnData/H5AD (Python)

This workshop will not go into python resources, but it is imporant to note that Python and R are the two most common programming languages for single-cell transcriptomic analysis and it is not uncommon to have to use both. The H5ad file format is similar to the Seurat object and SingleCellExperiment object in the sense that it stores the different components of the data in an organized manner.

Image from: https://www.sc-best-practices.org/introduction/analysis_tools.html


4. Assessing data quality

Any analysis, no matter of size, should undergo rigorous QC. When it comes to single-cell transcriptomic data, there are several computational tools that help you in the process from enabling visualizations to performing a panel of QC tests and reporting several different metrics for the end user to evaluate. A list of just some of these tools can be found here.

Let’s go through some of the most common QC metrics, what to they mean, and why do they matter?

4.1. Genes and transcripts

How does the number of detected genes and transcripts indicate quality? While we don’t have a known number of expressed genes for each cell type, we do know that cells require the expression of hundreds to thousands of genes to function properly.

Number of genes (features)

We can check these values by running the code below:

VlnPlot(seurat_object, features = 'nFeature_RNA') + NoLegend()

Here we can see that (1) the number of genes detected ranges from ~200 - 3000 and (2) most express ~900 genes. Is this normal and expected? In reality, there is no universal threshold, these values will depend on the cell type and the sequencing depth. But we do know that if most cells had extemely low counts that either cells were not healthy/intact or sequencing depth was much too low. It could also point to errors further upstream such as alignment to the incorrect reference genome.

Number of mRNA molecules (count)

We can also examine the total number of mRNA molecules detected:

VlnPlot(seurat_object, features = 'nCount_RNA')
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

As above, we can see a good distribution centered around 2500 mRNA molecules. These value should always be higher than the number of features.

Correlation of features and molecules

A third way to look at this data is to examine the correlation of features and mRNA molecules:

FeatureScatter(object = seurat_object, feature1 = 'nCount_RNA', feature2 = 'nFeature_RNA')

This metric is more informative because it tells you that as you detect more mRNA molecules, you also detect more different genes. This would generally be expected as you are getting a larger sampling of the mRNA resulting in an increased chance of sampling different genes. However, when you assess these plots, you will want to keep the biology in mind.

4.2. Mitochondrial RNA

Here, biological knowledge is extremely important for evaluating this metric and removing cells based on a threshold. Consider these three experiments:

  • Nuclei isolated from muscle cells which require a lot of energy

  • Muscle whole cells which require a lot of energy

  • Skin cells which are less metabolically active

What would you expect to see (qualitative) in the amount of mitochondrial genes for each of these experiments?

Assessing percent mitochondria counts

We can assess the percentage of mitochondrial genes by calculating the number of counts coming from mitochondrial genes (usually prefixed with mt- or MT-) compared to the total counts. This can be visualized like this:

# Calcuate the percentage of mitochondrial counts
seurat_object[["percent.mt"]] <- PercentageFeatureSet(seurat_object, pattern = "^MT-")

# Plot the values
VlnPlot(seurat_object, features = 'percent.mt')
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

We see that the percentage of counts representing mitochondrial genes is mostly below 5% but as high as ~25%. To evaluate these results think back to the three experiments and consider what you would expect to see:

  • Nuclei isolated from muscle cells which require a lot of energy

    Despite being metabolically active, nuclei isolation is expected to wash out mitochondria. High contamination is indicative of high ambient RNA or mitochondria sticking to the the nuclei.

  • Muscle whole cells which require a lot of energy

    Given that muscle cells are highly metabolically active and are known to have among the most mitochondria among mammalian cells, these data may show higher percentage of mitochondrial genes than most datasets.

  • Skin cells which are less metabolically active

    Given that these are less metabolically active, they may be expected to show similar or lower percentage of mitochondrial genes.

Correlation of counts and mitochondrial counts

As with the features and counts, we can look at how these correlate:

# Calcuate the percentage of mitochondrial counts
seurat_object[["percent.mt"]] <- PercentageFeatureSet(seurat_object, pattern = "^MT-")

# Plot the values
VlnPlot(seurat_object, features = 'percent.mt')
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.

Interestingly, these are not correlated at all. Prior to filtering, these values will be anti-correlated as mitochondria often makes up the ambient RNA (release from dead or lysed cells) and will be more represented in the background (low count cells).

4.3. Ambient RNA contamination

As with any single-cell transcriptomic tool, there are dozens, if not more, options for removal of abient RNA. Generally, the concept for ambient RNA removal is the same - substract genes which are present in the empty GEMs/wells from those which are found to have cells. Other techniques such as combinatorial barcoding approaches have to make different assumptions.

SoupX is one example of a tool which cleans up the soup (floating ambient RNA) from cells. This tools uses the most raw format of data which is generate before a threshold is set on the knee plot discussed above. For 10X Genomics these are stored in the raw_feature_bc_matrix and filtered_feature_bc_matrix folders.

This tutorial will not go through the entire ambient RNA removal workflow. Excellent examples can be found here and here.

4.4 Doublets

Another common challenge with using single-cell transcriptomic platforms is one that can affect most single-cell based techniques including flow cytometry - doublets. These can be a result of cells sticking to each other or being too close to each other during the sorting, and the rate is typically a function of the total number of cells examined (more cells results in more doublets).

Note: We will discuss dimension reduction which is central to this approach in a later module.

Doublets can consist of either 2 cells of the same type (homotypic) or different cell types (heterotypic). As you can probably imagine, distinguising homotypic doublets is more challenging, but are generally considered to be less of a concern.

DoubletFinder and scDblFinder are R based tool for the identification of doublets based on the general principle outlined above. This example uses scDblFinder:

sce = as.SingleCellExperiment(seurat_object) # We need to convert the Seurat format to SingleCellExperiment format
sce <- scDblFinder(sce) # Run scDblFinder

Next we will transfer the information gained from this analysis into the Seurat object so that we can visualize it a later step.

sce = as.SingleCellExperiment(seurat_object) # We need to convert the Seurat format to SingleCellExperiment format
Warning: Layer ‘data’ is emptyWarning: Layer ‘scale.data’ is empty
sce <- scDblFinder(sce) # Run scDblFinder
Creating ~5000 artificial doublets...
Dimensional reduction
Evaluating kNN...
Training model...
iter=0, 271 cells excluded from training.
iter=1, 227 cells excluded from training.
iter=2, 209 cells excluded from training.
Threshold found:0.518
109 (4%) doublets called

4.5. Filtering data

Ultimately, the purpose of the QC analyses is not only to understand your data and find any major issues. It is also used to identify individual data points which are likely problematic and could negatively impact subsequent analyses. Because we often have thousands of data points, we generally accept that some of these data points are technical noise which can simply be removed. For example, here we:

  • Remove cells with extremely high or low counts as either empty droplets or multiplets.

  • Remove cells with more than 5% of reads from mitochondrial genes as an indicator of unhealthy cells.

  • Remove cells classified as doublets.

seurat_object <- subset(seurat_object, subset = nFeature_RNA > 200 & nFeature_RNA < 2500 & percent.mt < 5 & scDblFinder.class == 'singlet')

print(seurat_object)
An object of class Seurat 
13714 features across 2529 samples within 1 assay 
Active assay: RNA (13714 features, 0 variable features)
 1 layer present: counts

5. Metadata!

We’ve invested a lot of effort, time, and money in generating and processing these data. From this we get tons of data which can be mined across several projects, labs, and institutions… or can we?

It is absolutely critical to not ignore the metadata slot of whatever single-cell object your are working with, both as a data generator and as a user of the data.

print(seurat_object@meta.data)

Can you see anything missing here? Consider this, could you describe the study design, model, and other important experimental details simply by looking at the information in this table?

Let’s start addressing some of the missing information

# Add missing species information
seurat_object$species = "NCBItaxon_9606"
seurat_object$species__ontology_label = "Homo sapiens"

# Add missing organ ontology
seurat_object$organ = "CL_2000001"
seurat_object$organ__ontology_label = "peripheral blood mononuclear cell"

# Add missing sex
seurat_object$sex = "unknown"

You will note that we use both ontologies and ontology labels. This is a good habit to truly ensure that your data is understood both my machines and humans, and minimized the risk of misunderstanding as each ontology is clearly defined. For more information see this useful resource about the Alexandria convention for metadata or the Ontology look service (OLS).

print(seurat_object@meta.data)

6. Normalization

Finally we want to address the issue of unequal sequencing depth and other technical noise by performing normalization. Here we normalize the data using a regularized negative binomial regression which models adjusts the counts for each gene based on the total number of counts within a cell with adjustments for lowly and highly expressed genes as described here.

seurat_object = SCTransform(seurat_object, verbose = F)
`vst.flavor` is set to 'v2' but could not find glmGamPoi installed.
Please install the glmGamPoi package for much faster estimation.
--------------------------------------------
install.packages('BiocManager')
BiocManager::install('glmGamPoi')
--------------------------------------------
Falling back to native (slower) implementation.

Warning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: alternation limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: alternation limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: NaNs producedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: iteration limit reachedWarning: alternation limit reached

Finally we want to visualize the data. To do this we need to perform dimension reduction which will be covered in a later module. We will run it here for visualization purposes.

seurat_object = RunPCA(seurat_object, verbose = F)
seurat_object = RunUMAP(seurat_object, dims = 1:30, verbose = F)
Warning: The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per sessionFound more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
FeaturePlot(seurat_object, features = c('scDblFinder.score', 'percent.mt', 'nFeature_RNA', 'nCount_RNA'))

DimPlot(seurat_object, group.by = c('species__ontology_label'))

LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBzY1JOQXNlcSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgSW50cm9kdWN0aW9uIHRvIHNjUk5Bc2VxIGFuZCBkYXRhIHByZS1wcm9jZXNzaW5nDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLiBGQVNUUSBGaWxlcywgZ2Vub21lIGFsaWdubWVudHMsIGFuZCBkZWNvbnZvbHV0aW9uIG9mIGNlbGxzDQoNClRoaXMgbW9kdWxlIG9mIHRoZSBDU1RNSSB3b3Jrc2hvcCBwcm92aWRlcyBhbiBvdmVydmlldyBvZiBrZXkgcXVhbGl0eSBjb250cm9sIChRQykgbWV0cmljcyBhbmQgcHJlLXByb2Nlc3Npbmcgc3RlcHMgaW4gdGhlIGluaXRpYWwgYW5hbHlzaXMgb2Ygc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWMgZGF0YSBbYmVnaW5uaW5nIGFmdGVyIGFsaWdubWVudCB0byBhIHJlZmVyZW5jZSBnZW5vbWVdey51bmRlcmxpbmV9LiBEZXRhaWxlZCB0dXRvcmlhbHMgZm9yIHByb2Nlc3NpbmcgcmF3IEJDTCBhbmQgRkFTVFEgZmlsZXMsIHBlcmZvcm1pbmcgZ2Vub21lIGFsaWdubWVudHMsIGFuZCBkZWNvbnZvbHV0aW5nIGNlbGxzIGZyb20gZW1wdHkgR0VNcy93ZWxscyBhcmUgYXZhaWxhYmxlIHRocm91Z2ggdmFyaW91cyByZXNvdXJjZXMsIHdlIHdpbGwgZm9jdXMgb24gdW5kZXJzdGFuZGluZyB3aGF0IGlzIGNhcHR1cmVkIGFuZCByZXByZXNlbnRlZCBpbiBzaW5nbGUtY2VsbCBkYXRhLg0KDQoqU2VlIGJlbG93IGZvciBsaW5rcyB0byBhbGlnbm1lbnQgYW5kIHByZWxpbWluYXJ5IGFuYWx5c2lzIHR1dG9yaWFsczoqDQoNCjo6OiB7c3R5bGU9ImJvcmRlcjogMnB4IHNvbGlkIGZvcmVzdGdyZWVuOyBiYWNrZ3JvdW5kLWNvbG9yIn0NCi0gICBWZW5kb3Itc3BlY2lmaWMgcGlwZWxpbmUgcmVzb3VyY2VzIChlLmcuLCBbMTBYIEdlbm9taWNzXShodHRwczovL3d3dy4xMHhnZW5vbWljcy5jb20vc3VwcG9ydC9zb2Z0d2FyZS9jZWxsLXJhbmdlci9sYXRlc3QvdHV0b3JpYWxzL2NyLXR1dG9yaWFsLWluKSkNCg0KLSAgIFtHYWxheHldKGh0dHBzOi8vdHJhaW5pbmcuZ2FsYXh5cHJvamVjdC5vcmcvdHJhaW5pbmctbWF0ZXJpYWwvdG9waWNzL3NpbmdsZS1jZWxsL3R1dG9yaWFscy9zY3JuYS1wcmVwcm9jZXNzaW5nL3R1dG9yaWFsLmh0bWwpDQoNCi0gICBHb29nbGUgY29sYWIgaGFuZHMtb24gZXhwZXJpZW5jZSAoWyp1bmRlciBjb25zdHJ1Y3RpbypuXXsudW5kZXJsaW5lfSkNCjo6Og0KDQpNb3N0IG9mIHRoZXNlIGBwaXBlbGluZXNgIHdpbGwgcHJvdmlkZSBhIHN1bW1hcnkgcmVwb3J0IHN1Y2ggYXMgdGhpcyBbMTBYIEdlbm9taWNzIHJlcG9ydF0oaHR0cHM6Ly9jZi4xMHhnZW5vbWljcy5jb20vc2FtcGxlcy9jZWxsLWV4cC83LjAuMS9TQzNwdjNfR0VYX0h1bWFuX1BCTUMvU0MzcHYzX0dFWF9IdW1hbl9QQk1DX3dlYl9zdW1tYXJ5Lmh0bWwpIG9yIHRoaXMgb25lIGZyb20gW1BhcnNlIEJpb3NjaWVuY2VzXShodHRwczovL2Nkbi5wYXJzZWJpb3NjaWVuY2VzLmNvbS9wYm1jL3YyLXZzLXYzL3YzLWFsbC1zYW1wbGVfYW5hbHlzaXNfc3VtbWFyeS5odG1sKS4gVGhlc2UgcmVwb3J0cyBwcm92aWRlIHRoZSBtb3N0IGJhc2ljIGluZm9ybWF0aW9uIHRvIGFzc2VzcyB0aGUgcXVhbGl0eSBvZiB0aGUgZGF0YSBzdGFydGluZyB3aXRoIHRoZSBbKmtuZWUgcGxvdCpdey51bmRlcmxpbmV9Lg0KDQojIyMgMS4xLiBVbmRlcnN0YW5kaW5nIHRoZSAna25lZSBwbG90Jw0KDQpVcG9uIHJlY2VpdmluZyBhbGlnbmVkIHNpbmdsZS1jZWxsL251Y2xlaSBzZXF1ZW5jaW5nIGRhdGEsIG9uZSBvZiB0aGUgaW5pdGlhbCBwbG90cyB0byBleGFtaW5lIGlzIHRoZSAna25lZSBwbG90LCcgY29tbW9ubHkgZ2VuZXJhdGVkIGR1cmluZyBhbmFseXNpcyBwaXBlbGluZXMuIFRoaXMgcGxvdCBoZWxwcyBkaXN0aW5ndWlzaCBiZXR3ZWVuIGJhcmNvZGVzIHJlcHJlc2VudGluZyBjZWxscyBhbmQgdGhvc2UgcmVwcmVzZW50aW5nIGJhY2tncm91bmQgbm9pc2Ugb3IgZW1wdHkgR2VsIEVtdWxzaW9uIGJlYWRzIChHRU1zKS4NCg0KKkdlbCBFbXVsc2lvbiBiZWFkcyAoR0VNcykqIGFyZSBtaWNyb2ZsdWlkaWMgZHJvcGxldHMgdXNlZCBpbiBwbGF0Zm9ybXMgbGlrZSAxMFggR2Vub21pY3MsIGVhY2ggcG90ZW50aWFsbHkgY29udGFpbmluZyBhIGNlbGwgb3IgbnVjbGVpLiBEaXN0aW5ndWlzaGluZyBiZXR3ZWVuIEdFTXMgd2l0aCBjZWxscyBhbmQgZW1wdHkgR0VNcyBpcyBjcnVjaWFsIGZvciBhY2N1cmF0ZSBhbmFseXNpcy4NCg0KVGhlIGtuZWUgcGxvdCB2aXN1YWxseSBkZXBpY3RzIGhvdyBhIHRocmVzaG9sZCBpcyBzZXQgdG8gZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIGNlbGwgYmFyY29kZXMgYW5kIGJhY2tncm91bmQgbm9pc2UuIEluIHBsYXRmb3JtcyBsaWtlIDEwWCBHZW5vbWljcywgY2VsbHMgd2l0aGluIEdFTXMgcHJvZHVjZSBhIGxhcmdlciBudW1iZXIgb2YgdW5pcXVlIG1STkEgbW9sZWN1bGVzIChVTUlzKSBjb21wYXJlZCB0byBlbXB0eSBHRU1zLiBUaGUga25lZSBwbG90IHR5cGljYWxseSBzaG93cyBhIHN1ZGRlbiBkcm9wIChrbmVlKSBpbiBVTUlzLCBpbmRpY2F0aW5nIHRoZSBzZXBhcmF0aW9uIGJldHdlZW4gR0VNcyBjb250YWluaW5nIGNlbGxzIGFuZCB0aG9zZSBsYWNraW5nIHRoZW0uDQoNClshW0ZpZ3VyZSBmcm9tIERhbmllbHNraSwgSy4gKDIwMjMpLiBHdWlkYW5jZSBvbiBQcm9jZXNzaW5nIHRoZSAxMHggR2Vub21pY3MgU2luZ2xlIENlbGwgR2VuZSBFeHByZXNzaW9uIEFzc2F5LiBJbjogQ2Fsb2dlcm8sIFIuQS4sIEJlbmVzLCBWLiAoZWRzKSBTaW5nbGUgQ2VsbCBUcmFuc2NyaXB0b21pY3MuIE1ldGhvZHMgaW4gTW9sZWN1bGFyIEJpb2xvZ3ksIHZvbCAyNTg0LiBIdW1hbmEsIE5ldyBZb3JrLCBOWS4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvOTc4LTEtMDcxNi0yNzU2LTNfMV0oZmlndXJlcy8xMHgtZHJvcHNlcS1leGFtcGxlLnBuZyl7c3R5bGU9ImJvcmRlcjoxcHggc29saWQgIzAwMDAwMDsgcGFkZGluZzozcHg7IG1hcmdpbjoycHgiIHdpZHRoPSI1NjQifV0oaHR0cHM6Ly9saW5rLnNwcmluZ2VyLmNvbS9wcm90b2NvbC8xMC4xMDA3Lzk3OC0xLTA3MTYtMjc1Ni0zXzEpDQoNCkludGVycHJldGluZyB0aGUga25lZSBwbG90IGludm9sdmVzIGlkZW50aWZ5aW5nIHRoaXMga25lZSBwb2ludC4gQSBkaXN0aW5jdCBrbmVlIHN1Z2dlc3RzIHN1Y2Nlc3NmdWwgc2VwYXJhdGlvbiwgd2hpbGUgdGhlIGFic2VuY2Ugb2YgYSBjbGVhciBrbmVlIGNvdWxkIGluZGljYXRlIGlzc3VlcyBzdWNoIGFzIGxvdyBSTkEgaW50ZWdyaXR5IG9yIGhpZ2ggYW1iaWVudCBSTkEgY29udGFtaW5hdGlvbi4NCg0KWyFbRmlndXJlIGZyb20gaHR0cHM6Ly9pc29zZXEuaG93L3VtaS9jZWxsLWNhbGxpbmcuaHRtbF0oZmlndXJlcy9rbmVlcGxvdC5wbmcpe3N0eWxlPSJib3JkZXI6MXB4IHNvbGlkICMwMDAwMDA7IHBhZGRpbmc6M3B4OyBtYXJnaW46MnB4IiB3aWR0aD0iNTUzIn1dKGh0dHBzOi8vaXNvc2VxLmhvdy91bWkvY2VsbC1jYWxsaW5nLmh0bWwpDQoNCk5vdyB0aGF0IHdlIGhhdmUgYSBnZW5lcmFsIGlkZWEgb2Ygd2hhdCB0byBsb29rIGZvciBpbiBhIHN1bW1hcnkgcmVwb3J0LCBsZXRzIHVzZSByZWFsIGRhdGEgdG8gc3RhcnQgZXhwbG9yaW5nIG1vcmUgYWR2YW5jZWQgUUMgbWV0cmljcyB3aGljaCByZXF1aXJlIGFkZGl0aW9uYWwgYW5hbHlzaXMgc3RlcHMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLiBTZXR0aW5nIHVwIG91ciBSIGVudmlyb25tZW50IGZvciBkYXRhIGV4cGxvcmF0aW9uDQoNCkluIHByZXBhcmF0aW9uIGZvciB0aGUgZm9sbG93aW5nIGhhbmRzLW9uIGV4cGVyaWVuY2VzIHdlIHdpbGwgc2V0IHVwIGFuIFIgZW52aXJvbm1lbnQgd2hpY2ggaGFzIGV2ZXJ5dGhpbmcgd2UgbmVlZC4NCg0KIyMjICoqMi4xLiBMb2FkIGxpYnJhcmllcyoqDQoNCmBgYHtyfQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsNCiAgbGlicmFyeShTZXVyYXQpDQogIGxpYnJhcnkoZ2dwbG90MikNCiAgbGlicmFyeSh0aWR5cikNCiAgbGlicmFyeShzY0RibEZpbmRlcikNCiAgbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkNCn0pDQpgYGANCg0KIyMjICoqMi4yLiBJbXBvcnQgZGF0YSoqDQoNCkZvciB0aGlzIHR1dG9yaWFsIHdlIHdpbGwgdXNlIHJhdyB1bnByb2Nlc3NlZCBkYXRhIGZyb20gaHVtYW4gcGVyaXBoZXJhbCBibG9vZCBtb25vbnVjbGVhciBjZWxscyAoUEJNQ3MpIGdlbmVyYXRlZCB1c2luZyB0aGUgMTBYIEdlbm9taWNzIHBsYXRmb3JtIHdoaWNoIGNhbiBiZSBkb3dubG9hZCBmcm9tIHRoZSBbU2V1cmF0IHBhZ2VdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvcGJtYzNrX3R1dG9yaWFsKS4gVGhlc2UgZGF0YSBzaG91bGQgYmUgZG93bmxvYWRlZCwgZXh0cmFjdGVkLCBhbmQgcGxhY2VkIGluIHRoZSBkb3dubG9hZHMgZm9sZGVyIGZvciB0aGlzIHdvcmtzaG9wLg0KDQpgYGB7cn0NCiMgTG9hZCB0aGUgUEJNQyBkYXRhc2V0DQpwYm1jLmRhdGEgPC0gUmVhZDEwWChkYXRhLmRpciA9ICIuL2Rvd25sb2Fkcy9maWx0ZXJlZF9nZW5lX2JjX21hdHJpY2VzL2hnMTkvIikNCiMgSW5pdGlhbGl6ZSB0aGUgU2V1cmF0IG9iamVjdCB3aXRoIHRoZSByYXcgKG5vbi1ub3JtYWxpemVkIGRhdGEpLg0Kc2V1cmF0X29iamVjdCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gcGJtYy5kYXRhLCBwcm9qZWN0ID0gInBibWMzayIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzID0gMywgbWluLmZlYXR1cmVzID0gMjAwKQ0KYGBgDQoNCipOb3RlOiBXZSBhcHBseSBzb21lIHByZWxpbWluYXJ5IGZpbHRlcnMgYXMgd2Uga25vdyB0aGF0IEdFTXMgd2hpY2ggdmVyeSBsb3cgbnVtYmVycyBvZiBnZW5lcyBvciBnZW5lcyB3aGljaCBhcmUgYmFyZWx5IGRldGVjdGVkIGFyZSB1bmxpa2VseSB0byBiZSBhY3R1YWwgY2VsbHMuKg0KDQojIyMgKioyLjMuIENoZWNrIHRoYXQgZXZlcnl0aGluZyB3YXMgbG9hZGVkIGNvcnJlY3RseSoqDQoNCmBgYHtyfQ0KcHJpbnQoc2V1cmF0X29iamVjdCkNCmBgYA0KDQojIyAzLiBPdmVydmlldyBvZiBTaW5nbGUtY2VsbCBEYXRhIFN0cnVjdHVyZQ0KDQpUbyBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgdG8gd29yayB3aXRoIHNpbmdsZS1jZWxsL251Y2xlaSBSTkEgc2VxdWVuY2luZyBkYXRhIHdlIHdpbGwgdGFrZSBhIGRlZXAgZGl2ZSBpbnRvIGhvdyBkYXRhc2V0cyBhcmUgKnR5cGljYWxseSogc3RydWN0dXJlZC4gSW4gdGhlIGRpYWdyYW0gYmVsb3cgd2UgaWxsdXN0cmF0ZSBob3cgZHJvcGxldC1iYXNlZCBzY1JOQXNlcSBhcHByb2FjaGVzIHNlcGFyYXRlIGNlbGxzIGFuZCB0aGVpciBtUk5Bcy4gQnV0IGhvdyBkb2VzIHRoaXMgdHJhbnNsYXRlIGludG8gWyoqKmRhdGEqKipdey51bmRlcmxpbmV9Pw0KDQohW0dlbmVyYXRlZCB1c2luZyBiaW9yZW5kZXIuY29tXShmaWd1cmVzL2NlbGxHRU1zLnBuZyl7c3R5bGU9ImJvcmRlcjoxcHggc29saWQgIzAwMDAwMDsgcGFkZGluZzozcHg7IG1hcmdpbjoycHgiIHdpZHRoPSI0NjkifQ0KDQojIyMgMy4xLiBUaGUgR2VuZSB4IENlbGwgTWF0cml4DQoNCmBgYHtyfQ0KIyBSYXcgY291bnRzIGluIGEgc2V1cmF0IG9iamVjdCBpcyBzYXZlZCBpbiB0aGUgYXNzYXlzLVJOQS1jb3VudHMgc2xvdCANCnJhd19jb3VudHMgPSBhcy5tYXRyaXgoc2V1cmF0X29iamVjdEBhc3NheXMkUk5BQGxheWVycyRjb3VudHMpDQpyb3duYW1lcyhyYXdfY291bnRzKSA9IHJvd25hbWVzKHNldXJhdF9vYmplY3QpDQpjb2xuYW1lcyhyYXdfY291bnRzKSA9IGNvbG5hbWVzKHNldXJhdF9vYmplY3QpDQoNCiMgTGV0cyBsb29rIGF0IHRoZSBmaXJzdCA0IGNvbHVtbnMgYW5kIHJvd3MNCnByaW50KHJhd19jb3VudHNbMTo0LDE6NF0pDQpgYGANCg0KKioqV2hhdCBkb2VzIHRoaXMgbWVhbj8qKioNCg0KKipDb2x1bW5zOioqIFRoZSBjb2x1bW5zIHJlcHJlc2VudCBhIHVuaXF1ZSBgYmFyY29kZWAuIEFzIHdlIGNhbiBzZWUgYmVsb3csIHRoaXMgYGJhcmNvZGVgIG1hcmtzIGFuIGluZGl2aWR1YWwgY2VsbCB3aGljaCBpcyBob3cgd2UgY2FuIGlkZW50aWZ5IGVhY2ggb25lLiBTbyBpbiB0aGlzIG9iamVjdCwgdGhlIGNvbHVtbnMgYXJlIHRoZSBjZWxscw0KDQohW0dlbmVyYXRlZCBpbiBiaW9yZW5kZXIuY29tIGFuZCBhZGFwdGVkIGZyb20gRGFuaWVsc2tpLCBLLiAoMjAyMykuIEd1aWRhbmNlIG9uIFByb2Nlc3NpbmcgdGhlIDEweCBHZW5vbWljcyBTaW5nbGUgQ2VsbCBHZW5lIEV4cHJlc3Npb24gQXNzYXkuIEluOiBDYWxvZ2VybywgUi5BLiwgQmVuZXMsIFYuIChlZHMpIFNpbmdsZSBDZWxsIFRyYW5zY3JpcHRvbWljcy4gTWV0aG9kcyBpbiBNb2xlY3VsYXIgQmlvbG9neSwgdm9sIDI1ODQuIEh1bWFuYSwgTmV3IFlvcmssIE5ZLiA8aHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvOTc4LTEtMDcxNi0yNzU2LTNfMT5dKGZpZ3VyZXMvUmVhZE92ZXJ2aWV3LnBuZyl7c3R5bGU9ImJvcmRlcjoxcHggc29saWQgIzAwMDAwMDsgcGFkZGluZzozcHg7IG1hcmdpbjoycHgiIHdpZHRoPSI4MzQifQ0KDQoqKlJvd3M6KiogV2Ugc2VlIHRoYXQgdGhlIGZpcnN0IGZldyByb3dzIGhhdmUgbmFtZXMgc3VjaCBhcyAqUlAxMS0yMDZMMTAuOS4qIEEgcXVpY2sgKmdvb2dsZSogc2VhcmNoIHdpbGwgdGVsbCB5b3UgdGhhdCB0aGlzIGlzIGEgaHVtYW4gZ2VuZSAoRW5zZW1ibCBJRCAtIEVOU0cwMDAwMDIzNzQ5MSkuIFNvIHdlIHNlZSBpbiB0aGlzIG9iamVjdCB0aGF0IHRoZSByb3dzIHJlcHJlc2VudCB0aGUgZ2VuZXMuDQoNCiMjIyAzLjIuIENvdW50aW5nIHRoZSBudW1iZXIgb2YgY2VsbHMgYW5kIGdlbmVzDQoNClNpbmdsZS1jZWxsIHRyYW5zY3JpcHRvbWljIGFuYWx5c2lzIHRvb2xzICgqZS5nLiosIFNldXJhdCkgZG9lcyBhIGxvdCBvZiB0aGUgaGVhdnkgbGlmdGluZyBmb3IgdXMgd2hlbiBpdCBjb21lcyB0byBjb3VudHMgYW5kIG90aGVyIHN1bW1hcnkgZGF0YSBpbmZvcm1hdGlvbi4gRm9yIGV4YW1wbGUsIHRoZSBmb2xsb3dpbmcgY29kZSB3aWxsIHRlbGwgeW91IHRoZSBudW1iZXIgb2YgZ2VuZXMgYW5kIGNlbGxzLCBhcyB3ZWxsIHN0b3JlIHJhdywgbm9ybWFsaXplZCwgYW5kIHNjYWxlZCBkYXRhLCBkaW1lbnNpb24gcmVkdWN0aW9ucywgYW5kIG90aGVyIGFuYWx5c2VzIHdoaWNoIG1heSBoYXZlIGJlZW4gcGVyZm9ybWVkLg0KDQpgYGB7cn0NCnByaW50KHNldXJhdF9vYmplY3QpDQpgYGANCg0KVGhpcyBpbmRpY2F0ZXMgdGhhdCB3ZSBoYXZlIDEzLDcxNCBnZW5lcyAoZmVhdHVyZXMpIGFjcm9zcyAyLDcwMCBzYW1wbGVzLg0KDQpIb3dldmVyLCB3ZSBjYW4gYWxzbyBleHRyYWN0IHRoYXQgaW5mb3JtYXRpb24gZnJvbSB0aGUgY291bnQgdGFibGU6DQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUwKCdDZWxsczogJywgbmNvbChyYXdfY291bnRzKSkpDQpwcmludChwYXN0ZTAoJ0ZlYXR1cmVzOicsIG5yb3cocmF3X2NvdW50cykpKQ0KYGBgDQoNClRoZXNlIG51bWJlcnMgc2hvdWxkIG1hdGNoIQ0KDQojIyMgMy4zLiBBIGRlZXBlciBkaXZlIGludG8gY291bnRzDQoNCiMjIyMgMy4zYS4gV2hhdCBpcyBzZXF1ZW5jaW5nIGRlcHRoPw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCByZWFkcyBpbiBlYWNoIGluZGl2aWR1YWwgY2VsbA0KcmVhZF9kZXB0aHMgPSBjb2xTdW1zKHJhd19jb3VudHMpDQoNCiMgSG93IGFyZSB0aGV5IGRpc3RyaWJ1dGVkPw0KaGlzdChyZWFkX2RlcHRocywgbWFpbiA9ICJIaXN0b2dyYW0gb2YgcmVhZCBkZXB0aCIsIA0KICAgICB4bGFiID0gIkRlcHRoIiwgeWxhYiA9ICJGcmVxdWVuY3kiLA0KICAgICBicmVha3MgPSAyMDApDQpgYGANCg0KVGhpcyBoaXN0b2dyYW0gc2hvd3MgdGhhdCB0aGUgbnVtYmVyIG9mIG1STkEgdHJhbnNjcmlwdHMgZGV0ZWN0ZWQgaW4gZWFjaCBjZWxsIHJhbmdlZCBmcm9tIDU0NiB0byAxNSw4MTgsIGJ1dCB0aGF0IG1vc3Qgd2VyZSBhcm91bmQgMiwwMDAuIFNvIHdoYXQgbWlnaHQgYWNjb3VudCBmb3IgdGhlc2UgZGlmZmVyZW5jZXM/DQoNCi0gICAqKkNlbGwgdHlwZXM6KiogU29tZSBjZWxsIHR5cGVzIG1heSBzaW1wbHkganVzdCBleHByZXNzIG1vcmUgbVJOQSB0aGFuIG90aGVycy4gRm9yIGV4YW1wbGUsIEhlcGF0b2N5dGVzIGFyZSBrbm93biB0byBjb250YWluIGEgbG90IG9mIFJOQSB3aGlsZSBvdGhlcnMgbWF5IG5vdC4NCg0KLSAgICoqU3RvY2hhc3RpYyBnZW5lIGV4cHJlc3Npb246KiogRmx1Y3R1YXRpb25zIGluIHRyYW5zY3JpcHRpb24gYW5kIGRlZ3JhZGF0aW9uIGZyb20gbm9ybWFsIGJpb2xvZ2ljYWwgcHJvY2Vzc2VzLCBjZWxsIHN0YXRlcywgYW5kIG90aGVyIGZhY3RvcnMgaW4gaW5kaXZpZHVhbCBjZWxscyB3aGljaCBoYXZlIHZlcnkgbG93IGFtb3VudHMgb2YgUk5BIHRvIGJlZ2luIGNhbiBhcHBlYXIgYXMgYmlnIGRpZmZlcmVuY2VzLg0KDQotICAgKipUZWNobmljYWwgdmFyaWFiaWxpdHk6KiogU2VxdWVuY2luZyBkZXB0aCwgYXNzYXkga2l0cywgYW5kIG90aGVyIHRlY2huaWNhbCBmYWN0b3JzIG1heSBjb250cmlidXRlIHRvIHRoZXNlIGRpZmZlcmVuY2VzLg0KDQpbKlRoZSB6ZXJvLWluZmxhdGlvbiBkZWJhdGUqXXsudW5kZXJsaW5lfSo6IEluIHRoZSBlYXJseSBkYXlzIG9mIHNjUk5Bc2VxIGl0IHdhcyBhIHdpZGVseSBhY2NlcHRlZCB0aGF0IGRhdGEgd2FzIHplcm8taW5mbGF0ZWQsIGkuZS4sIGl0IGhhZCBtb3JlIHplcm8ncyB0aGFuIGl0IHNob3VsZCBiZWNhdXNlIG9mIHRlY2huaWNhbCBjaGFsbGVuZ2VzLiBUb2RheSwgd2UgYmV0dGVyIHVuZGVyc3RhbmQgdGhhdCB0aGUgbGFyZ2UgbnVtYmVyIG9mIHplcm8ncyBhcmUgbm90IG9ubHkgdGVjaG5pY2FsLCBidXQgaW4gZmFjdCBkb2VzIHJlZmxlY3QgdHJ1ZSBiaW9sb2d5IGFzIHdlbGwuIFRoZSBmb2xsb3dpbmcgbWFudXNjcmlwdCBkZWx2ZXMgZGVlcGVyIGludG8gdGhlIGRlYmF0ZTogW0ppYW5nIGV0IGFsLiwgMjAyMl0oaHR0cHM6Ly9nZW5vbWViaW9sb2d5LmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvczEzMDU5LTAyMi0wMjYwMS01KSoNCg0KIyMjIyAzLjNiLiBBbm90aGVyIHdheSB0byBsb29rIGF0IGRlcHRoDQoNClNlcXVlbmNpbmcgZGVwdGggdWx0aW1hdGVseSBkcml2ZXMgdGhlIGFiaWxpdHkgdG8gZGV0ZWN0IGluZGl2aWR1YWwgZ2VuZXMgaW4gYSBjZWxsIG9yIHNhbXBsZSAoYnVsayBSTkEgc2VxKS4gQXMgbW9yZSByZWFkcyBhcmUgcGVyZm9ybWVkIG9uIGEgc2FtcGxlLCB0aGUgcHJvYmFiaWxpdHkgb2YgY2FwdHVyaW5nIGEgbG93ZXIgYWJ1bmRhbmNlIGdlbmUgaW5jcmVhc2VzLiBUaGVyZWZvcmUsIHNlcXVlbmNpbmcgZGVwdGggY2FuIGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3Qgb24gdGhlIGVuZCByZXN1bHRzIG9mIHlvdXIgc2VxdWVuY2luZyByZXN1bHRzLg0KDQoqU28gd2h5IG5vdCBzZXF1ZW5jZSBhcyBkZWVwIGFzIHBvc3NpYmU/KiBBcyB3aXRoIGFueXRoaW5nLCB3ZSBuZWVkIHRvIGJhbGFuY2UgY29zdCB3aXRoIHZhbHVlLiBUaGUgZm9sbG93aW5nIGZpZ3VyZXMgYXJlIHN0YW5kYXJkIG91dHB1dHMgZnJvbSBzY1JOQXNlcSByZXBvcnRzIGluY2x1ZGluZyB0aG9zZSBsaW5rZWQgYWJvdmUuDQoNCiFbXShpbWFnZXMvVW50aXRsZWQlMjAoOCkucG5nKXtzdHlsZT0iYm9yZGVyOjFweCBzb2xpZCAjMDAwMDAwOyBwYWRkaW5nOjNweDsgbWFyZ2luOjJweCIgd2lkdGg9IjcwOCJ9DQoNCkFzIHlvdSBjYW4gc2VlLCB0aGUgbW9yZSByZWFkcyB3ZSBoYXZlIHBlciBjZWxsLCB0aGUgc21hbGxlciB0aGUgc2xvcCBmb3IgY2FwdHVyaW5nIG5ldyBnZW5lcyBnZXRzLiBUaGVyZWZvcmUsIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gY2FwdHVyaW5nIDgwJSBvZiBnZW5lcyBhbmQgMTAwJSBjb3VsZCBtZWFuIDIsIDEwLCBvciBldmVuIFw+MTAwWCB0aGUgYW1vdW50IG9mIHNlcXVlbmNpbmcuIElzIGl0IHRydWx5IHdvcnRoIGl0IGZvciBjYXB0dXJpbmcgYSBnZW5lIHdobyBleHByZXNzaW9uIGlzIHNvIGxvdz8gVGhpcyBpcyB3aHkgd2UgbGltaXQgb3VyIGRlcHRoIGZvciBzZXF1ZW5jaW5nIGFuZCB3aHkgbW9zdCBkYXRhc2V0cyBwcm9kdWNlZCB3aXRoIHRoZSBzYW1wbGUgcGxhdGZvcm0gd2lsbCB0ZW5kIHRvIGRldGVjdCBhIHNpbWlsYXIgbnVtYmVyIG9mIGdlbmVzLg0KDQojIyMgMy40LiBDb21tb24gU3RvcmFnZSBGb3JtYXRzDQoNCiMjIyMgMy40YS4gU3BhcnNlIHRhYmxlcyAocmF3KQ0KDQpXZSBwcmV2aW91c2x5IG1lbnRpb25lZCB0aGF0IHNpbmdsZS1jZWxsL251Y2xlaSB0cmFuc2NyaXB0b21pYyBkYXRhIGhhcyBhIGxvdCBvZiB6ZXJvcy4gTGV0cyBhY3R1YWxseSBsb29rIGF0IHRoZSBjb3VudCBkaXN0cmlidXRpb24gc28geW91IGNhbiBzZWU6DQoNCmBgYHtyfQ0KIyBXZSB3YW4gdG8gY29sbGFwc2UgYWxsIHRoZSB2YWx1ZXMgaW50byBhIHZlY3RvciBvZiB2YWx1ZXMNCm1hdF92YWx1ZXMgPC0gYXMudmVjdG9yKHJhd19jb3VudHMpDQoNCiMgUGxvdCBoaXN0b2dyYW0NCmhpc3QobWF0X3ZhbHVlcywgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTWF0cml4IFZhbHVlcyIsIHhsYWIgPSAiVmFsdWUiLCB5bGFiID0gIkZyZXF1ZW5jeSIsIGJyZWFrcyA9IDYwMCwgeGxpbSA9IGMoMCwxMCkpDQpgYGANCg0KVGhpcyBpcyB3aHkgeW91IHdpbGwgYWxtb3N0IGFsd2F5cyBmaW5kIHJhdyBzY1JOQXNlcSBkYXRhIHNhdmVkIGFzIGEgYC5tdHhgIChzcGFyc2UgbWF0cml4KSBmaWxlIG9yIGZvcm1hdC4NCg0KSGVyZSB3ZSB3aWxsIGRvd25sb2FkIHB1YmxpY2x5IGFjY2Vzc2libGUgc3BhcnNlIG1hdHJpY2VzIGZyb20gdGhlIFtHZW5lIEV4cHJlc3Npb24gT21uaWJ1c10oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8pLCBhbiBleGNlbGxlbnQgcmVzb3VyY2UgZm9yIG9idGFpbmluZyByYXcgZGF0YSBpZiB5b3Ugd2FudCB0byByZXVzZSBhbmQgcmVhbmFseXplIGV4aXN0aW5nIGRhdGEuIFRoaXMgZGF0YXNldCBpcyBmcm9tIFtzaW5nbGUtbnVjbGVpIFJOQSBzZXF1ZW5jaW5nIG9mIGxpdmVycyBmcm9tIG1pY2UgdHJlYXRlZCB3aXRoIHRoZSBlbnZpcm9ubWVudGFsIGNvbnRhbWluYW50IDIsMyw3LDgtdGV0cmFjaGxvcm9kaWJlbnpvLSpwKi1kaW94aW4gKFRDREQpXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0UxODQ1MDYpLg0KDQpgYGB7cn0NCiNkb3dubG9hZC5maWxlKCdodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9kb3dubG9hZC8/YWNjPUdTRTE4NDUwNiZmb3JtYXQ9ZmlsZSZmaWxlPUdTRTE4NDUwNiU1RnNjdCUyRW10eCUyRWd6JywgJy4vZG93bmxvYWRzL21hdHJpeC5tdHguZ3onKQ0Kc3lzdGVtKCJndW56aXAgLi9kb3dubG9hZHMvbWF0cml4Lm10eC5neiIpDQoNCiNkb3dubG9hZC5maWxlKCdodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9kb3dubG9hZC8/YWNjPUdTRTE4NDUwNiZmb3JtYXQ9ZmlsZSZmaWxlPUdTRTE4NDUwNiU1RnNjdCUyRWJhcmNvZGVzJTJFdHN2JTJFZ3onLCAnLi9kb3dubG9hZHMvYmFyY29kZXMudHN2Lmd6JykNCnN5c3RlbSgiZ3VuemlwIC4vZG93bmxvYWRzL2JhcmNvZGVzLnRzdi5neiIpDQoNCiNkb3dubG9hZC5maWxlKCdodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9kb3dubG9hZC8/YWNjPUdTRTE4NDUwNiZmb3JtYXQ9ZmlsZSZmaWxlPUdTRTE4NDUwNiU1RnNjdCUyRWdlbmVzJTJFdHN2JTJFZ3onLCAnLi9kb3dubG9hZHMvZ2VuZXMudHN2Lmd6JykNCnN5c3RlbSgiZ3VuemlwIC4vZG93bmxvYWRzL2dlbmVzLnRzdi5neiIpDQpgYGANCg0KYGBge3J9DQojIFJlYWQgdGhlIGZpcnN0IGZldyBsaW5lcyBvZiB0aGUgZmlsZQ0KbGluZXMgPC0gcmVhZExpbmVzKCcuL2Rvd25sb2Fkcy9tYXRyaXgubXR4JywgbiA9IDEwKQ0KDQojIFByaW50IHRoZSBsaW5lcw0KY2F0KGxpbmVzLCBzZXAgPSAiXG4iKQ0KYGBgDQoNCkFzIHlvdSBjYW4gc2VlLCB0aGVyZSBpcyBub3QgYSB6ZXJvIGluIHNpZ2h0IGhlcmUuIEFmdGVyIHlvdSByZWFkIHRoZSBmaXJzdCAyIHN1bW1hcnkgbGluZXMgeW91IGNhbiBzZWUgdGhlIGZvbGxvd2luZyBzdHJ1Y3R1cmU6DQoNCnwgRmVhdHVyZS9HZW5lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEJhcmNvZGUvQ2VsbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBDb3VudC9WYWx1ZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBSZWZlcnMgdG8gdGhlIHJvdyBudW1iZXIgaW4gdGhlIGdlbmVzLnRzdiBmaWxlIHwgUmVmZXJzIHRvIHRoZSByb3cgbnVtYmVyIGluIHRoZSBiYXJjb2Rlcy50c3YgZmlsZSB8IEluY2x1ZGUgYWxsIG5vbi16ZXJvIHZhbHVlcyBvZiB0aGUgZGF0YXNldCB8DQoNCkxldCdzIHNlZSB3aGF0IHRoaXMgaXMgaGVyZQ0KDQpgYGB7cn0NCiMgUmVhZCB0aGUgc3BlY2lmaWMgbGluZSBvZiB0aGUgZmlsZQ0KcmVhZExpbmVzKCcuL2Rvd25sb2Fkcy9nZW5lcy50c3YnLCBuID0gMTUpWzE1XQ0KDQojIFJlYWQgdGhlIHNwZWNpZmljIGxpbmUgb2YgdGhlIGZpbGUNCnJlYWRMaW5lcygnLi9kb3dubG9hZHMvYmFyY29kZXMudHN2JywgbiA9IDEpWzFdDQoNCmBgYA0KDQpGcm9tIGhlcmUgd2UgY2FuIHNlZSB0aGF0IHRoZSBmaXJzdCByb3cgb2YgZGF0YSBpbiB0aGUgYG10eGAgZmlsZSByZXByZXNlbnQgdGhlIGdlbmUgKioqU250ZzEqKiogZm9yIHRoZSBjZWxsIHdpdGggYmFyY29kZSAqKipBQUFDQ0NBQ0FBQ1RUQ1RUXzEqKiouDQoNCiMjIyMgMy40Yi4gU2V1cmF0IG9iamVjdA0KDQpbIVtJbWFnZSBmcm9tIGh0dHBzOi8vc2liLXN3aXNzLmdpdGh1Yi5pby9zaW5nbGUtY2VsbC10cmFpbmluZy9kYXkxL2RheTEtMl9hbmFseXNpc190b29sc19xYy5odG1sXShmaWd1cmVzL3NldXJhdF9vYmplY3QucG5nKXtzdHlsZT0iYm9yZGVyOjFweCBzb2xpZCAjMDAwMDAwOyBwYWRkaW5nOjNweDsgbWFyZ2luOjJweCIgd2lkdGg9IjYxMyJ9XShodHRwczovL3NpYi1zd2lzcy5naXRodWIuaW8vc2luZ2xlLWNlbGwtdHJhaW5pbmcvZGF5MS9kYXkxLTJfYW5hbHlzaXNfdG9vbHNfcWMuaHRtbCkNCg0KU2V1cmF0IG9iamVjdHMgYXJlIHByb2JhYmx5IHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgdG8gc3RvcmUgYW5kIG9yZ2FuaXplIHNjUk5Bc2VxIGRhdGEgZm9yIFIgdXNlcnMuIFNvIGZhciB0aGlzIGlzIHdoYXQgd2UgaGF2ZSB1c2VkIGZvciB0aGUgcHVibGlzaGVkIFBCTUMgZGF0YSB3aGljaCB3ZSBpbXBvcnRlZCBpbiAqU2VjdGlvbiAyKi4gTGV0J3MgdGFrZSBhbm90aGVyIGNsb3NlciBsb29rIGF0IHRoaXMgb2JqZWN0Og0KDQpgYGB7cn0NCnByaW50KHNsb3ROYW1lcyhzZXVyYXRfb2JqZWN0KSkgIyBMZXRzIGV4YW1pbmUgYWxsIHRoZSBzbG90cw0KYGBgDQoNClRoZXJlIGlzIGEgbG90IG9mIGluZm9ybWF0aW9uLCBidXQgdGhlIG9uZXMgeW91IHdpbGwgbW9zdCBjb21tb25seSB1c2UgaWYgeW91IGRvIHNpbmdsZS1jZWxsIGFuYWx5c2VzIGluIFIgYXJlIGBhc3NheXNgLCBgbWV0YS5kYXRhYCwgYW5kIGByZWR1Y3Rpb25zYA0KDQotICAgKioqQXNzYXlzKioqIHdpbGwgaG9sZCB0aGUgY291bnQgZGF0YQ0KDQotICAgKioqTWV0YS5kYXRhKioqIHdpbGwgaG9sZCB0aGUgbWV0YWRhdGEgZm9yIGVhY2ggaW5kaXZpZHVhbCBjZWxsL251Y2xlaQ0KDQotICAgKioqUmVkdWN0aW9ucyoqKiB3aWxsIGhvbGQgdGhlIGRpbWVuc2lvbiByZWR1Y3Rpb25zIGZvciB2aXN1YWxpemF0aW9ucw0KDQojIyMjIDMuNGMuIFNpbmdsZUNlbGxFeHBlcmltZW50DQoNCkFub3RoZXIgY29tbW9uIGZvcm1hdCBpbiBSIGFyZSBTaW5nbGVDZWxsRXhwZXJpbWVudHMuIFRoZXkgY29udGFpbiB0aGUgc2FtZSBpbmZvcm1hdGlvbiwgYnV0IGFyZSBvcmdhbml6ZWQgaW4gYSBkaWZmZXJlbnQgbWFubmVyLg0KDQpbIVtpbWFnZSBmcm9tIGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9ib29rcy8zLjEzL09TQ0EuaW50cm8vdGhlLXNpbmdsZWNlbGxleHBlcmltZW50LWNsYXNzLmh0bWxdKGZpZ3VyZXMvU2luZ2xlQ2VsbEV4cGVyaW1lbnQucG5nKXtzdHlsZT0iYm9yZGVyOjFweCBzb2xpZCAjMDAwMDAwOyBwYWRkaW5nOjNweDsgbWFyZ2luOjJweCIgd2lkdGg9IjYxOCJ9XShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xMy9PU0NBLmludHJvL3RoZS1zaW5nbGVjZWxsZXhwZXJpbWVudC1jbGFzcy5odG1sKQ0KDQpGaXJzdCwgbGV0J3MgY29udmVydCBvdXIgc2V1cmF0IG9iamVjdCB0byBTaW5nbGVDZWxsRXhwZXJpbWVudC4gTW9zdCBmb3JtYXRzIGNhbiBlYXNpbHkgYmUgaW50ZXJjaGFuZ2VkOg0KDQpgYGB7cn0NCnNjZSA9IGFzLlNpbmdsZUNlbGxFeHBlcmltZW50KHNldXJhdF9vYmplY3QpDQpgYGANCg0KTmV4dCwgbGV0cyBzZWUgd2hhdCB0aGUgc3RydWN0dXJlIGxvb2tzIGxpa2U6DQoNCmBgYHtyfQ0KcHJpbnQoc2NlKQ0KYGBgDQoNCkhlcmUgd2UgY2FuIHNlZToNCg0KLSAgICoqcm93bmFtZXMqKiBhbmQgcm93RGF0YSBjb250YWluIHRoZSBnZW5lIGluZm9ybWF0aW9uIGFuZCBtZXRhZGF0YQ0KDQotICAgKipjb2xuYW1lcyoqIGFuZCBjb2xEYXRhIGNvbnRhaW4gdGhlIGNlbGwgaW5mb3JtYXRpb24gYW5kIG1ldGFkYXRhDQoNCi0gICAqKmFzc2F5cyoqIGhlcmUgYWxzbyBob2xkIHRoZSBjb3VudCBkYXRhDQoNCldlIGNhbiBleGFtaW5lIHRoZSBwaWVjZXMgb2YgZGF0YSBzdWNoIGFzIHRoZSBjZWxsIG1ldGFkYXRhIGFzIGZvbGxvd3M6DQoNCmBgYHtyfQ0KcHJpbnQoY29sRGF0YShzY2UpKQ0KYGBgDQoNCiMjIyMgMy40ZC4gQW5uRGF0YS9INUFEIChQeXRob24pDQoNClRoaXMgd29ya3Nob3Agd2lsbCBub3QgZ28gaW50byBweXRob24gcmVzb3VyY2VzLCBidXQgaXQgaXMgaW1wb3JhbnQgdG8gbm90ZSB0aGF0IFB5dGhvbiBhbmQgUiBhcmUgdGhlIHR3byBtb3N0IGNvbW1vbiBwcm9ncmFtbWluZyBsYW5ndWFnZXMgZm9yIHNpbmdsZS1jZWxsIHRyYW5zY3JpcHRvbWljIGFuYWx5c2lzIGFuZCBpdCBpcyBub3QgdW5jb21tb24gdG8gaGF2ZSB0byB1c2UgYm90aC4gVGhlIEg1YWQgZmlsZSBmb3JtYXQgaXMgc2ltaWxhciB0byB0aGUgU2V1cmF0IG9iamVjdCBhbmQgU2luZ2xlQ2VsbEV4cGVyaW1lbnQgb2JqZWN0IGluIHRoZSBzZW5zZSB0aGF0IGl0IHN0b3JlcyB0aGUgZGlmZmVyZW50IGNvbXBvbmVudHMgb2YgdGhlIGRhdGEgaW4gYW4gb3JnYW5pemVkIG1hbm5lci4NCg0KWyFbSW1hZ2UgZnJvbTogaHR0cHM6Ly93d3cuc2MtYmVzdC1wcmFjdGljZXMub3JnL2ludHJvZHVjdGlvbi9hbmFseXNpc190b29scy5odG1sXShmaWd1cmVzL2FubmRhdGEuanBnKXtzdHlsZT0iYm9yZGVyOjFweCBzb2xpZCAjMDAwMDAwOyBwYWRkaW5nOjNweDsgbWFyZ2luOjJweCIgd2lkdGg9IjQzMCJ9XShodHRwczovL3d3dy5zYy1iZXN0LXByYWN0aWNlcy5vcmcvaW50cm9kdWN0aW9uL2FuYWx5c2lzX3Rvb2xzLmh0bWwpDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LiBBc3Nlc3NpbmcgZGF0YSBxdWFsaXR5DQoNCkFueSBhbmFseXNpcywgbm8gbWF0dGVyIG9mIHNpemUsIHNob3VsZCB1bmRlcmdvIHJpZ29yb3VzIFFDLiBXaGVuIGl0IGNvbWVzIHRvIHNpbmdsZS1jZWxsIHRyYW5zY3JpcHRvbWljIGRhdGEsIHRoZXJlIGFyZSBzZXZlcmFsIGNvbXB1dGF0aW9uYWwgdG9vbHMgdGhhdCBoZWxwIHlvdSBpbiB0aGUgcHJvY2VzcyBmcm9tIGVuYWJsaW5nIHZpc3VhbGl6YXRpb25zIHRvIHBlcmZvcm1pbmcgYSBwYW5lbCBvZiBRQyB0ZXN0cyBhbmQgcmVwb3J0aW5nIHNldmVyYWwgZGlmZmVyZW50IG1ldHJpY3MgZm9yIHRoZSBlbmQgdXNlciB0byBldmFsdWF0ZS4gQSBsaXN0IG9mIGp1c3Qgc29tZSBvZiB0aGVzZSB0b29scyBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vd3d3LnNjcm5hLXRvb2xzLm9yZy90b29scz9zb3J0PW5hbWUmY2F0cz1RdWFsaXR5Q29udHJvbCkuDQoNCkxldCdzIGdvIHRocm91Z2ggc29tZSBvZiB0aGUgbW9zdCBjb21tb24gUUMgbWV0cmljcywgd2hhdCB0byB0aGV5IG1lYW4sIGFuZCB3aHkgZG8gdGhleSBtYXR0ZXI/DQoNCiMjIyA0LjEuIEdlbmVzIGFuZCB0cmFuc2NyaXB0cw0KDQoqSG93IGRvZXMgdGhlIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcyBhbmQgdHJhbnNjcmlwdHMgaW5kaWNhdGUgcXVhbGl0eT8qIFdoaWxlIHdlIGRvbid0IGhhdmUgYSBrbm93biBudW1iZXIgb2YgZXhwcmVzc2VkIGdlbmVzIGZvciBlYWNoIGNlbGwgdHlwZSwgd2UgZG8ga25vdyB0aGF0IGNlbGxzIHJlcXVpcmUgdGhlIGV4cHJlc3Npb24gb2YgaHVuZHJlZHMgdG8gdGhvdXNhbmRzIG9mIGdlbmVzIHRvIGZ1bmN0aW9uIHByb3Blcmx5Lg0KDQoqKk51bWJlciBvZiBnZW5lcyAoZmVhdHVyZXMpKioNCg0KV2UgY2FuIGNoZWNrIHRoZXNlIHZhbHVlcyBieSBydW5uaW5nIHRoZSBjb2RlIGJlbG93Og0KDQpgYGB7cn0NClZsblBsb3Qoc2V1cmF0X29iamVjdCwgZmVhdHVyZXMgPSAnbkZlYXR1cmVfUk5BJykgKyBOb0xlZ2VuZCgpDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlIHRoYXQgKDEpIHRoZSBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgcmFuZ2VzIGZyb20gXH4yMDAgLSAzMDAwIGFuZCAoMikgbW9zdCBleHByZXNzIFx+OTAwIGdlbmVzLiAqSXMgdGhpcyBub3JtYWwgYW5kIGV4cGVjdGVkPyogSW4gcmVhbGl0eSwgdGhlcmUgaXMgbm8gdW5pdmVyc2FsIHRocmVzaG9sZCwgdGhlc2UgdmFsdWVzIHdpbGwgZGVwZW5kIG9uIHRoZSBjZWxsIHR5cGUgYW5kIHRoZSBzZXF1ZW5jaW5nIGRlcHRoLiBCdXQgd2UgZG8ga25vdyB0aGF0IGlmIG1vc3QgY2VsbHMgaGFkIGV4dGVtZWx5IGxvdyBjb3VudHMgdGhhdCBlaXRoZXIgY2VsbHMgd2VyZSBub3QgaGVhbHRoeS9pbnRhY3Qgb3Igc2VxdWVuY2luZyBkZXB0aCB3YXMgbXVjaCB0b28gbG93LiBJdCBjb3VsZCBhbHNvIHBvaW50IHRvIGVycm9ycyBmdXJ0aGVyIHVwc3RyZWFtIHN1Y2ggYXMgYWxpZ25tZW50IHRvIHRoZSBpbmNvcnJlY3QgcmVmZXJlbmNlIGdlbm9tZS4NCg0KKipOdW1iZXIgb2YgbVJOQSBtb2xlY3VsZXMgKGNvdW50KSoqDQoNCldlIGNhbiBhbHNvIGV4YW1pbmUgdGhlIHRvdGFsIG51bWJlciBvZiBtUk5BIG1vbGVjdWxlcyBkZXRlY3RlZDoNCg0KYGBge3J9DQpWbG5QbG90KHNldXJhdF9vYmplY3QsIGZlYXR1cmVzID0gJ25Db3VudF9STkEnKQ0KYGBgDQoNCkFzIGFib3ZlLCB3ZSBjYW4gc2VlIGEgZ29vZCBkaXN0cmlidXRpb24gY2VudGVyZWQgYXJvdW5kIDI1MDAgbVJOQSBtb2xlY3VsZXMuIFRoZXNlIHZhbHVlIHNob3VsZCBhbHdheXMgYmUgaGlnaGVyIHRoYW4gdGhlIG51bWJlciBvZiBmZWF0dXJlcy4NCg0KKipDb3JyZWxhdGlvbiBvZiBmZWF0dXJlcyBhbmQgbW9sZWN1bGVzKioNCg0KQSB0aGlyZCB3YXkgdG8gbG9vayBhdCB0aGlzIGRhdGEgaXMgdG8gZXhhbWluZSB0aGUgY29ycmVsYXRpb24gb2YgZmVhdHVyZXMgYW5kIG1STkEgbW9sZWN1bGVzOg0KDQpgYGB7cn0NCkZlYXR1cmVTY2F0dGVyKG9iamVjdCA9IHNldXJhdF9vYmplY3QsIGZlYXR1cmUxID0gJ25Db3VudF9STkEnLCBmZWF0dXJlMiA9ICduRmVhdHVyZV9STkEnKQ0KYGBgDQoNClRoaXMgbWV0cmljIGlzIG1vcmUgaW5mb3JtYXRpdmUgYmVjYXVzZSBpdCB0ZWxscyB5b3UgdGhhdCBhcyB5b3UgZGV0ZWN0IG1vcmUgbVJOQSBtb2xlY3VsZXMsIHlvdSBhbHNvIGRldGVjdCBtb3JlIGRpZmZlcmVudCBnZW5lcy4gVGhpcyB3b3VsZCBnZW5lcmFsbHkgYmUgZXhwZWN0ZWQgYXMgeW91IGFyZSBnZXR0aW5nIGEgbGFyZ2VyIHNhbXBsaW5nIG9mIHRoZSBtUk5BIHJlc3VsdGluZyBpbiBhbiBpbmNyZWFzZWQgY2hhbmNlIG9mIHNhbXBsaW5nIGRpZmZlcmVudCBnZW5lcy4gSG93ZXZlciwgd2hlbiB5b3UgYXNzZXNzIHRoZXNlIHBsb3RzLCB5b3Ugd2lsbCB3YW50IHRvIFsqKmtlZXAgdGhlIGJpb2xvZ3kgaW4gbWluZCoqXXsudW5kZXJsaW5lfS4NCg0KIyMjIDQuMi4gTWl0b2Nob25kcmlhbCBSTkENCg0KSGVyZSwgYmlvbG9naWNhbCBrbm93bGVkZ2UgaXMgZXh0cmVtZWx5IGltcG9ydGFudCBmb3IgZXZhbHVhdGluZyB0aGlzIG1ldHJpYyBhbmQgcmVtb3ZpbmcgY2VsbHMgYmFzZWQgb24gYSB0aHJlc2hvbGQuIENvbnNpZGVyIHRoZXNlIHRocmVlIGV4cGVyaW1lbnRzOg0KDQotICAgTnVjbGVpIGlzb2xhdGVkIGZyb20gbXVzY2xlIGNlbGxzIHdoaWNoIHJlcXVpcmUgYSBsb3Qgb2YgZW5lcmd5DQoNCi0gICBNdXNjbGUgd2hvbGUgY2VsbHMgd2hpY2ggcmVxdWlyZSBhIGxvdCBvZiBlbmVyZ3kNCg0KLSAgIFNraW4gY2VsbHMgd2hpY2ggYXJlIGxlc3MgbWV0YWJvbGljYWxseSBhY3RpdmUNCg0KKldoYXQgd291bGQgeW91IGV4cGVjdCB0byBzZWUgKHF1YWxpdGF0aXZlKSBpbiB0aGUgYW1vdW50IG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMgZm9yIGVhY2ggb2YgdGhlc2UgZXhwZXJpbWVudHM/Kg0KDQoqKkFzc2Vzc2luZyBwZXJjZW50IG1pdG9jaG9uZHJpYSBjb3VudHMqKg0KDQpXZSBjYW4gYXNzZXNzIHRoZSBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMgYnkgY2FsY3VsYXRpbmcgdGhlIG51bWJlciBvZiBjb3VudHMgY29taW5nIGZyb20gbWl0b2Nob25kcmlhbCBnZW5lcyAodXN1YWxseSBwcmVmaXhlZCB3aXRoIG10LSBvciBNVC0pIGNvbXBhcmVkIHRvIHRoZSB0b3RhbCBjb3VudHMuIFRoaXMgY2FuIGJlIHZpc3VhbGl6ZWQgbGlrZSB0aGlzOg0KDQpgYGB7cn0NCiMgQ2FsY3VhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCBjb3VudHMNCnNldXJhdF9vYmplY3RbWyJwZXJjZW50Lm10Il1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KHNldXJhdF9vYmplY3QsIHBhdHRlcm4gPSAiXk1ULSIpDQoNCiMgUGxvdCB0aGUgdmFsdWVzDQpWbG5QbG90KHNldXJhdF9vYmplY3QsIGZlYXR1cmVzID0gJ3BlcmNlbnQubXQnKQ0KDQpgYGANCg0KV2Ugc2VlIHRoYXQgdGhlIHBlcmNlbnRhZ2Ugb2YgY291bnRzIHJlcHJlc2VudGluZyBtaXRvY2hvbmRyaWFsIGdlbmVzIGlzIG1vc3RseSBiZWxvdyA1JSBidXQgYXMgaGlnaCBhcyBcfjI1JS4gVG8gZXZhbHVhdGUgdGhlc2UgcmVzdWx0cyB0aGluayBiYWNrIHRvIHRoZSB0aHJlZSBleHBlcmltZW50cyBhbmQgY29uc2lkZXIgd2hhdCB5b3Ugd291bGQgZXhwZWN0IHRvIHNlZToNCg0KLSAgIE51Y2xlaSBpc29sYXRlZCBmcm9tIG11c2NsZSBjZWxscyB3aGljaCByZXF1aXJlIGEgbG90IG9mIGVuZXJneQ0KDQogICAgRGVzcGl0ZSBiZWluZyBtZXRhYm9saWNhbGx5IGFjdGl2ZSwgbnVjbGVpIGlzb2xhdGlvbiBpcyBleHBlY3RlZCB0byB3YXNoIG91dCBtaXRvY2hvbmRyaWEuIEhpZ2ggY29udGFtaW5hdGlvbiBpcyBpbmRpY2F0aXZlIG9mIGhpZ2ggYW1iaWVudCBSTkEgb3IgbWl0b2Nob25kcmlhIHN0aWNraW5nIHRvIHRoZSB0aGUgbnVjbGVpLg0KDQotICAgTXVzY2xlIHdob2xlIGNlbGxzIHdoaWNoIHJlcXVpcmUgYSBsb3Qgb2YgZW5lcmd5DQoNCiAgICBHaXZlbiB0aGF0IG11c2NsZSBjZWxscyBhcmUgaGlnaGx5IG1ldGFib2xpY2FsbHkgYWN0aXZlIGFuZCBhcmUga25vd24gdG8gaGF2ZSBhbW9uZyB0aGUgbW9zdCBtaXRvY2hvbmRyaWEgYW1vbmcgbWFtbWFsaWFuIGNlbGxzLCB0aGVzZSBkYXRhIG1heSBzaG93IGhpZ2hlciBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMgdGhhbiBtb3N0IGRhdGFzZXRzLg0KDQotICAgU2tpbiBjZWxscyB3aGljaCBhcmUgbGVzcyBtZXRhYm9saWNhbGx5IGFjdGl2ZQ0KDQogICAgR2l2ZW4gdGhhdCB0aGVzZSBhcmUgbGVzcyBtZXRhYm9saWNhbGx5IGFjdGl2ZSwgdGhleSBtYXkgYmUgZXhwZWN0ZWQgdG8gc2hvdyBzaW1pbGFyIG9yIGxvd2VyIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCBnZW5lcy4NCg0KKipDb3JyZWxhdGlvbiBvZiBjb3VudHMgYW5kIG1pdG9jaG9uZHJpYWwgY291bnRzKioNCg0KQXMgd2l0aCB0aGUgZmVhdHVyZXMgYW5kIGNvdW50cywgd2UgY2FuIGxvb2sgYXQgaG93IHRoZXNlIGNvcnJlbGF0ZToNCg0KYGBge3J9DQpGZWF0dXJlU2NhdHRlcihzZXVyYXRfb2JqZWN0LCBmZWF0dXJlMSA9ICduQ291bnRfUk5BJywgZmVhdHVyZTIgPSAncGVyY2VudC5tdCcpDQpgYGANCg0KSW50ZXJlc3RpbmdseSwgdGhlc2UgYXJlIG5vdCBjb3JyZWxhdGVkIGF0IGFsbC4gUHJpb3IgdG8gZmlsdGVyaW5nLCB0aGVzZSB2YWx1ZXMgd2lsbCBiZSBhbnRpLWNvcnJlbGF0ZWQgYXMgbWl0b2Nob25kcmlhIG9mdGVuIG1ha2VzIHVwIHRoZSBhbWJpZW50IFJOQSAocmVsZWFzZSBmcm9tIGRlYWQgb3IgbHlzZWQgY2VsbHMpIGFuZCB3aWxsIGJlIG1vcmUgcmVwcmVzZW50ZWQgaW4gdGhlIGJhY2tncm91bmQgKGxvdyBjb3VudCBjZWxscykuDQoNCiMjIyA0LjMuIEFtYmllbnQgUk5BIGNvbnRhbWluYXRpb24NCg0KQXMgd2l0aCBhbnkgc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWMgdG9vbCwgdGhlcmUgYXJlIGRvemVucywgaWYgbm90IG1vcmUsIG9wdGlvbnMgZm9yIHJlbW92YWwgb2YgYWJpZW50IFJOQS4gR2VuZXJhbGx5LCB0aGUgY29uY2VwdCBmb3IgYW1iaWVudCBSTkEgcmVtb3ZhbCBpcyB0aGUgc2FtZSAtIHN1YnN0cmFjdCBnZW5lcyB3aGljaCBhcmUgcHJlc2VudCBpbiB0aGUgKmVtcHR5KiBHRU1zL3dlbGxzIGZyb20gdGhvc2Ugd2hpY2ggYXJlIGZvdW5kIHRvIGhhdmUgY2VsbHMuIE90aGVyIHRlY2huaXF1ZXMgc3VjaCBhcyBjb21iaW5hdG9yaWFsIGJhcmNvZGluZyBhcHByb2FjaGVzIGhhdmUgdG8gbWFrZSBkaWZmZXJlbnQgYXNzdW1wdGlvbnMuDQoNCiFbPGh0dHBzOi8vd3d3LnNjLWJlc3QtcHJhY3RpY2VzLm9yZy9wcmVwcm9jZXNzaW5nX3Zpc3VhbGl6YXRpb24vcXVhbGl0eV9jb250cm9sLmh0bWw+XShmaWd1cmVzL2FtYmllbnRSTkEucG5nKXt3aWR0aD0iNTIwIn0NCg0KW1NvdXBYXShodHRwczovL2FjYWRlbWljLm91cC5jb20vZ2lnYXNjaWVuY2UvYXJ0aWNsZS85LzEyL2dpYWExNTEvNjA0OTgzMSkgaXMgb25lIGV4YW1wbGUgb2YgYSB0b29sIHdoaWNoIGNsZWFucyB1cCB0aGUgKnNvdXAqIChmbG9hdGluZyBhbWJpZW50IFJOQSkgZnJvbSBjZWxscy4gVGhpcyB0b29scyB1c2VzIHRoZSBtb3N0IHJhdyBmb3JtYXQgb2YgZGF0YSB3aGljaCBpcyBnZW5lcmF0ZSBiZWZvcmUgYSB0aHJlc2hvbGQgaXMgc2V0IG9uIHRoZSBrbmVlIHBsb3QgZGlzY3Vzc2VkIGFib3ZlLiBGb3IgMTBYIEdlbm9taWNzIHRoZXNlIGFyZSBzdG9yZWQgaW4gdGhlIGByYXdfZmVhdHVyZV9iY19tYXRyaXhgIGFuZCBgZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXhgIGZvbGRlcnMuDQoNClRoaXMgdHV0b3JpYWwgd2lsbCBub3QgZ28gdGhyb3VnaCB0aGUgZW50aXJlIGFtYmllbnQgUk5BIHJlbW92YWwgd29ya2Zsb3cuIEV4Y2VsbGVudCBleGFtcGxlcyBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vY2VsbGdlbmkuZ2l0aHViLmlvL25vdGVib29rcy9odG1sL25ldy0xMGtQQk1DLVNvdXBYLmh0bWwpIGFuZCBbaGVyZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1NvdXBYL3ZpZ25ldHRlcy9wYm1jVHV0b3JpYWwuaHRtbCkuDQoNCiMjIyA0LjQgRG91YmxldHMNCg0KQW5vdGhlciBjb21tb24gY2hhbGxlbmdlIHdpdGggdXNpbmcgc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWMgcGxhdGZvcm1zIGlzIG9uZSB0aGF0IGNhbiBhZmZlY3QgbW9zdCBzaW5nbGUtY2VsbCBiYXNlZCB0ZWNobmlxdWVzIGluY2x1ZGluZyBmbG93IGN5dG9tZXRyeSAtIGRvdWJsZXRzLiBUaGVzZSBjYW4gYmUgYSByZXN1bHQgb2YgY2VsbHMgc3RpY2tpbmcgdG8gZWFjaCBvdGhlciBvciBiZWluZyB0b28gY2xvc2UgdG8gZWFjaCBvdGhlciBkdXJpbmcgdGhlICpzb3J0aW5nKiwgYW5kIHRoZSByYXRlIGlzIHR5cGljYWxseSBhIGZ1bmN0aW9uIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgY2VsbHMgZXhhbWluZWQgKG1vcmUgY2VsbHMgcmVzdWx0cyBpbiBtb3JlIGRvdWJsZXRzKS4NCg0KIVs8aHR0cHM6Ly93d3cuc2MtYmVzdC1wcmFjdGljZXMub3JnL3ByZXByb2Nlc3NpbmdfdmlzdWFsaXphdGlvbi9xdWFsaXR5X2NvbnRyb2wuaHRtbD5dKGZpZ3VyZXMvRG91YmxldHMucG5nKXt3aWR0aD0iOTk2In0NCg0KKk5vdGU6IFdlIHdpbGwgZGlzY3VzcyBkaW1lbnNpb24gcmVkdWN0aW9uIHdoaWNoIGlzIGNlbnRyYWwgdG8gdGhpcyBhcHByb2FjaCBpbiBhIGxhdGVyIG1vZHVsZS4qDQoNCkRvdWJsZXRzIGNhbiBjb25zaXN0IG9mIGVpdGhlciAyIGNlbGxzIG9mIHRoZSBzYW1lIHR5cGUgKGhvbW90eXBpYykgb3IgZGlmZmVyZW50IGNlbGwgdHlwZXMgKGhldGVyb3R5cGljKS4gQXMgeW91IGNhbiBwcm9iYWJseSBpbWFnaW5lLCBkaXN0aW5ndWlzaW5nIGhvbW90eXBpYyBkb3VibGV0cyBpcyBtb3JlIGNoYWxsZW5naW5nLCBidXQgYXJlIGdlbmVyYWxseSBjb25zaWRlcmVkIHRvIGJlIGxlc3Mgb2YgYSBjb25jZXJuLg0KDQpbRG91YmxldEZpbmRlcl0oaHR0cHM6Ly93d3cuY2VsbC5jb20vY2VsbC1zeXN0ZW1zL2Z1bGx0ZXh0L1MyNDA1LTQ3MTIoMTkpMzAwNzMtMCkgYW5kIFtzY0RibEZpbmRlcl0oaHR0cHM6Ly93d3cuY2VsbC5jb20vY2VsbC1zeXN0ZW1zL2Z1bGx0ZXh0L1MyNDA1LTQ3MTIoMTkpMzAwNzMtMCkgYXJlIFIgYmFzZWQgdG9vbCBmb3IgdGhlIGlkZW50aWZpY2F0aW9uIG9mIGRvdWJsZXRzIGJhc2VkIG9uIHRoZSBnZW5lcmFsIHByaW5jaXBsZSBvdXRsaW5lZCBhYm92ZS4gVGhpcyBleGFtcGxlIHVzZXMgc2NEYmxGaW5kZXI6DQoNCmBgYHtyfQ0Kc2NlID0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQoc2V1cmF0X29iamVjdCkgIyBXZSBuZWVkIHRvIGNvbnZlcnQgdGhlIFNldXJhdCBmb3JtYXQgdG8gU2luZ2xlQ2VsbEV4cGVyaW1lbnQgZm9ybWF0DQpzY2UgPC0gc2NEYmxGaW5kZXIoc2NlKSAjIFJ1biBzY0RibEZpbmRlcg0KYGBgDQoNCk5leHQgd2Ugd2lsbCB0cmFuc2ZlciB0aGUgaW5mb3JtYXRpb24gZ2FpbmVkIGZyb20gdGhpcyBhbmFseXNpcyBpbnRvIHRoZSBTZXVyYXQgb2JqZWN0IHNvIHRoYXQgd2UgY2FuIHZpc3VhbGl6ZSBpdCBhIGxhdGVyIHN0ZXAuDQoNCmBgYHtyfQ0KIyBUcmFuc2ZlciB0aGUgZG91YmxldCBpbmZvcm1hdGlvbiB0byB0aGUgc2V1cmF0IG9iamVjdA0Kc2V1cmF0X29iamVjdCRzY0RibEZpbmRlci5jbGFzcyA9IGNvbERhdGEoc2NlKSRzY0RibEZpbmRlci5jbGFzcw0Kc2V1cmF0X29iamVjdCRzY0RibEZpbmRlci5zY29yZSA9IGNvbERhdGEoc2NlKSRzY0RibEZpbmRlci5zY29yZQ0KDQpgYGANCg0KIyMjIDQuNS4gRmlsdGVyaW5nIGRhdGENCg0KVWx0aW1hdGVseSwgdGhlIHB1cnBvc2Ugb2YgdGhlIFFDIGFuYWx5c2VzIGlzIG5vdCBvbmx5IHRvIHVuZGVyc3RhbmQgeW91ciBkYXRhIGFuZCBmaW5kIGFueSBtYWpvciBpc3N1ZXMuIEl0IGlzIGFsc28gdXNlZCB0byBpZGVudGlmeSBpbmRpdmlkdWFsIGRhdGEgcG9pbnRzIHdoaWNoIGFyZSBsaWtlbHkgcHJvYmxlbWF0aWMgYW5kIGNvdWxkIG5lZ2F0aXZlbHkgaW1wYWN0IHN1YnNlcXVlbnQgYW5hbHlzZXMuIEJlY2F1c2Ugd2Ugb2Z0ZW4gaGF2ZSB0aG91c2FuZHMgb2YgZGF0YSBwb2ludHMsIHdlIGdlbmVyYWxseSBhY2NlcHQgdGhhdCBzb21lIG9mIHRoZXNlIGRhdGEgcG9pbnRzIGFyZSB0ZWNobmljYWwgbm9pc2Ugd2hpY2ggY2FuIHNpbXBseSBiZSByZW1vdmVkLiBGb3IgZXhhbXBsZSwgaGVyZSB3ZToNCg0KLSAgIFJlbW92ZSBjZWxscyB3aXRoIGV4dHJlbWVseSBoaWdoIG9yIGxvdyBjb3VudHMgYXMgZWl0aGVyIGVtcHR5IGRyb3BsZXRzIG9yIG11bHRpcGxldHMuDQoNCi0gICBSZW1vdmUgY2VsbHMgd2l0aCBtb3JlIHRoYW4gNSUgb2YgcmVhZHMgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzIGFzIGFuIGluZGljYXRvciBvZiB1bmhlYWx0aHkgY2VsbHMuDQoNCi0gICBSZW1vdmUgY2VsbHMgY2xhc3NpZmllZCBhcyBkb3VibGV0cy4NCg0KYGBge3J9DQpzZXVyYXRfb2JqZWN0IDwtIHN1YnNldChzZXVyYXRfb2JqZWN0LCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiAyMDAgJiBuRmVhdHVyZV9STkEgPCAyNTAwICYgcGVyY2VudC5tdCA8IDUgJiBzY0RibEZpbmRlci5jbGFzcyA9PSAnc2luZ2xldCcpDQoNCnByaW50KHNldXJhdF9vYmplY3QpDQpgYGANCg0KIyMgNS4gTWV0YWRhdGEhDQoNCldl4oCZdmUgaW52ZXN0ZWQgYSBsb3Qgb2YgZWZmb3J0LCB0aW1lLCBhbmQgbW9uZXkgaW4gZ2VuZXJhdGluZyBhbmQgcHJvY2Vzc2luZyB0aGVzZSBkYXRhLiBGcm9tIHRoaXMgd2UgZ2V0ICp0b25zKiBvZiBkYXRhIHdoaWNoIGNhbiBiZSBtaW5lZCBhY3Jvc3Mgc2V2ZXJhbCBwcm9qZWN0cywgbGFicywgYW5kIGluc3RpdHV0aW9uc+KApiAqb3IgY2FuIHdlPyoNCg0KSXQgaXMgYWJzb2x1dGVseSBjcml0aWNhbCB0byBub3QgaWdub3JlIHRoZSBtZXRhZGF0YSBzbG90IG9mIHdoYXRldmVyIHNpbmdsZS1jZWxsIG9iamVjdCB5b3VyIGFyZSB3b3JraW5nIHdpdGgsIGJvdGggYXMgYSBkYXRhIGdlbmVyYXRvciBhbmQgYXMgYSB1c2VyIG9mIHRoZSBkYXRhLg0KDQpgYGB7cn0NCnByaW50KHNldXJhdF9vYmplY3RAbWV0YS5kYXRhKQ0KYGBgDQoNCkNhbiB5b3Ugc2VlIGFueXRoaW5nIG1pc3NpbmcgaGVyZT8gQ29uc2lkZXIgdGhpcywgY291bGQgeW91IGRlc2NyaWJlIHRoZSBzdHVkeSBkZXNpZ24sIG1vZGVsLCBhbmQgb3RoZXIgaW1wb3J0YW50IGV4cGVyaW1lbnRhbCBkZXRhaWxzIHNpbXBseSBieSBsb29raW5nIGF0IHRoZSBpbmZvcm1hdGlvbiBpbiB0aGlzIHRhYmxlPw0KDQpMZXQncyBzdGFydCBhZGRyZXNzaW5nIHNvbWUgb2YgdGhlIG1pc3NpbmcgaW5mb3JtYXRpb24NCg0KYGBge3J9DQojIEFkZCBtaXNzaW5nIHNwZWNpZXMgaW5mb3JtYXRpb24NCnNldXJhdF9vYmplY3Qkc3BlY2llcyA9ICJOQ0JJdGF4b25fOTYwNiINCnNldXJhdF9vYmplY3Qkc3BlY2llc19fb250b2xvZ3lfbGFiZWwgPSAiSG9tbyBzYXBpZW5zIg0KDQojIEFkZCBtaXNzaW5nIG9yZ2FuIG9udG9sb2d5DQpzZXVyYXRfb2JqZWN0JG9yZ2FuID0gIkNMXzIwMDAwMDEiDQpzZXVyYXRfb2JqZWN0JG9yZ2FuX19vbnRvbG9neV9sYWJlbCA9ICJwZXJpcGhlcmFsIGJsb29kIG1vbm9udWNsZWFyIGNlbGwiDQoNCiMgQWRkIG1pc3Npbmcgc2V4DQpzZXVyYXRfb2JqZWN0JHNleCA9ICJ1bmtub3duIg0KYGBgDQoNCllvdSB3aWxsIG5vdGUgdGhhdCB3ZSB1c2UgYm90aCBvbnRvbG9naWVzIGFuZCBvbnRvbG9neSBsYWJlbHMuIFRoaXMgaXMgYSBnb29kIGhhYml0IHRvIHRydWx5IGVuc3VyZSB0aGF0IHlvdXIgZGF0YSBpcyB1bmRlcnN0b29kIGJvdGggbXkgbWFjaGluZXMgYW5kIGh1bWFucywgYW5kIG1pbmltaXplZCB0aGUgcmlzayBvZiBtaXN1bmRlcnN0YW5kaW5nIGFzIGVhY2ggb250b2xvZ3kgaXMgY2xlYXJseSBkZWZpbmVkLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBzZWUgW3RoaXMgdXNlZnVsIHJlc291cmNlIGFib3V0IHRoZSBBbGV4YW5kcmlhIGNvbnZlbnRpb24gZm9yIG1ldGFkYXRhXShodHRwczovL3NpbmdsZWNlbGwuemVuZGVzay5jb20vaGMvZW4tdXMvc2VjdGlvbnMvNDQwMjY1MjQ1NDA0My1NZXRhZGF0YS1GaWxlKSBvciB0aGUgW09udG9sb2d5IGxvb2sgc2VydmljZSAoT0xTKV0oaHR0cHM6Ly93d3cuZWJpLmFjLnVrL29sczQpLg0KDQpgYGB7cn0NCnByaW50KHNldXJhdF9vYmplY3RAbWV0YS5kYXRhKQ0KYGBgDQoNCiMjIDYuIE5vcm1hbGl6YXRpb24NCg0KRmluYWxseSB3ZSB3YW50IHRvIGFkZHJlc3MgdGhlIGlzc3VlIG9mIHVuZXF1YWwgc2VxdWVuY2luZyBkZXB0aCBhbmQgb3RoZXIgdGVjaG5pY2FsIG5vaXNlIGJ5IHBlcmZvcm1pbmcgbm9ybWFsaXphdGlvbi4gSGVyZSB3ZSBub3JtYWxpemUgdGhlIGRhdGEgdXNpbmcgYSAqcmVndWxhcml6ZWQgbmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbiogd2hpY2ggbW9kZWxzIGFkanVzdHMgdGhlIGNvdW50cyBmb3IgZWFjaCBnZW5lIGJhc2VkIG9uIHRoZSB0b3RhbCBudW1iZXIgb2YgY291bnRzIHdpdGhpbiBhIGNlbGwgd2l0aCBhZGp1c3RtZW50cyBmb3IgbG93bHkgYW5kIGhpZ2hseSBleHByZXNzZWQgZ2VuZXMgYXMgW2Rlc2NyaWJlZCBoZXJlXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE5LTE4NzQtMSkuDQoNCmBgYHtyfQ0Kc2V1cmF0X29iamVjdCA9IFNDVHJhbnNmb3JtKHNldXJhdF9vYmplY3QsIHZlcmJvc2UgPSBGKQ0KYGBgDQoNCg0KRmluYWxseSB3ZSB3YW50IHRvIHZpc3VhbGl6ZSB0aGUgZGF0YS4gVG8gZG8gdGhpcyB3ZSBuZWVkIHRvIHBlcmZvcm0gZGltZW5zaW9uIHJlZHVjdGlvbiB3aGljaCB3aWxsIGJlIGNvdmVyZWQgaW4gYSBsYXRlciBtb2R1bGUuIFdlIHdpbGwgcnVuIGl0IGhlcmUgZm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZXMuDQoNCmBgYHtyfQ0Kc2V1cmF0X29iamVjdCA9IFJ1blBDQShzZXVyYXRfb2JqZWN0LCB2ZXJib3NlID0gRikNCnNldXJhdF9vYmplY3QgPSBSdW5VTUFQKHNldXJhdF9vYmplY3QsIGRpbXMgPSAxOjMwLCB2ZXJib3NlID0gRikNCkZlYXR1cmVQbG90KHNldXJhdF9vYmplY3QsIGZlYXR1cmVzID0gYygnc2NEYmxGaW5kZXIuc2NvcmUnLCAncGVyY2VudC5tdCcsICduRmVhdHVyZV9STkEnLCAnbkNvdW50X1JOQScpKQ0KYGBgDQoNCmBgYHtyfQ0KRGltUGxvdChzZXVyYXRfb2JqZWN0LCBncm91cC5ieSA9IGMoJ3NwZWNpZXNfX29udG9sb2d5X2xhYmVsJykpDQpgYGANCg==